#!/usr/bin/python3
# -*- encoding: utf-8 -*-
"""
@File    :   wwdns.py
@Time    :   2022/03/27 00:13:44
@Author  :   不愿意透露姓名的刘易先生
@Version :   1.0
@Contact :   faith01238@hotmail.com
@Homepage : https://liumou.site'
@Desc    :   自动配置DNS
"""
import inspect
import ipaddress
import os
import platform
import re
import sys
from argparse import ArgumentParser
from datetime import datetime
from os import system, path, environ, getcwd, chdir, getenv
from subprocess import getstatusoutput, getoutput
from sys import exit


def ListRemoveNone(ls: list):
	"""
	移除列表中的空元素
	:param ls: 需要移除的列表
	:return: list
	"""
	tmp = []
	for i in ls:
		if str(i) != "" or str(i) != " " or i is not None:
			tmp.append(tmp)
	return tmp


def ListGetLen(ls: list, n: int):
	"""
	获取指定长度范围的元素列表
	:param n: 指定长度(移除小于该长度的元素)
	:param ls: 需要处理的列表
	:return: list
	"""
	tmp = []
	for i in ls:
		if len(i) > n:
			tmp.append(i)
	return tmp


class ColorLogger:
	def __init__(self, file=None, txt=False, class_name=None, cover=False, fileinfo=False, basename=True):
		"""
		初始化日志模块
		:param file: 设置日志文件
		:param txt: 是否启用文本记录功能
		:param class_name: 调用的Class名称
		:param cover: 当使用文本记录的时候，是否覆盖原内容
		:param fileinfo: 是否显示日志文件信息
		:param basename: 设置日志文件显示信息,True(只显示文件名), False(显示绝对路径)
		"""

		self.Red = "\033[31m"  # 红色
		self.Greet = "\033[32m"  # 绿色
		self.Yellow = '\033[33m'  # 黄色
		self.Blue = '\033[34m'  # 蓝色
		self.RESET_ALL = '\033[0m'  # 清空颜色
		self.basename = basename
		self.fileinfo = fileinfo
		self.cover = cover
		self.class_name = class_name
		# 是否启用txt
		self.txt_mode = txt
		# 日志文件显示名称
		self.file_name = None
		# 日志文件绝对路径
		self.file_path = file
		# 日期
		self.date = str(datetime.now()).split('.')[0]
		# 行数
		self.line_ = 1
		# 模块名称
		self.module_name = None
		# 文件名称
		self.filename = None
		# 日志内容
		self.msg1 = None
		# 是否启用文件名
		self.format_filename = True
		# 是否启用日期
		self.format_date = True
		# 是否启用时间
		self.format_time = True
		# 是否显示类名称
		self.format_class = True
		# 是否显示函数名称
		self.format_fun = True
		# 是否显示行数
		self.format_line = True
		# 是否显示 等级
		self.format_level = True
		# 设置等级关系
		self.level_dic = {"DEBUG": 0, "INFO": 1, "WARNING": 2, "ERROR": 3}
		# 设置文件记录最低等级
		self.level_text = 0
		# 设置控制台显示最低等级
		self.level_console = 0
		# 初始化实例参数
		self._init_fun()

	def _init_fun(self):
		"""
		初始化函数
		:return:
		"""
		#
		if self.file_path is None and self.txt_mode:
			if platform.system().lower() == 'linux'.lower():
				file = path.join(environ['HOME'], 'ColorInfo.log')
			else:
				file = path.join(environ["USERPROFILE"], 'ColorInfo.log')
			self.file = str(file)
			self.file_path = path.abspath(self.file)
		else:
			self.file_name = " "
		# 如果开启了文本记录模式,则实例化一个读写
		if self.txt_mode:
			try:
				# 日志写入实例
				self.txt_wr = open(file=self.file_path, mode='a+', encoding='utf-8')
				if self.txt_mode:
					self.txt_wr.close()
					self.txt_wr = open(file=self.file_path, mode='a+', encoding='utf-8')
					if self.cover:
						self.txt_wr.close()
						self.txt_wr = open(file=self.file_path, mode='w+', encoding='utf-8')
			except Exception as e:
				print(e)
				exit(1)
			if self.basename:
				self.file_name = path.basename(str(self.file_path))

	def set_format(self, date_on=True, time_on=True, filename_on=True, class_on=True, fun=True, line=True, level=True):
		"""
		设置格式开关,默认全开
		:param level: 是否显示等级(默认: True)-DEBUG
		:param line: 是否显示行号(默认: True)-line: 230
		:param fun: 是否显示函数(默认: True)
		:param class_on: 是否显示类(默认: True)
		:param date_on: 是否显示日期(默认: True)-2022-11-03
		:param time_on: 是否显示时间(默认: True)-20:42:24
		:param filename_on: 是否显示文件名(源码文件)(默认: True)-ColorInfo.py
		:return:
		"""
		self.format_date = date_on
		self.format_filename = filename_on
		self.format_time = time_on
		self.format_class = class_on
		self.format_fun = fun
		self.format_line = line
		self.format_level = level

	def set_level(self, console="DEBUG", text="DEBUG"):
		"""
		设置显示等级,当实际等级低于设置等级的时候将不会显示/写入
		:param console: 设置控制台显示最低等级(DEBUG/INFO/WARNING/ERROR)
		:param text: 设置文本记录最低等级(DEBUG/INFO/WARNING/ERROR)
		:return:
		"""
		level_list = ["DEBUG", "INFO", "WARNING", "ERROR"]
		if console.upper() not in level_list:
			console = "DEBUG"
		if text.upper() not in level_list:
			text = "DEBUG"
		self.level_console = self.level_dic[console.upper()]
		self.level_text = self.level_dic[text.upper()]

	def fun_info(self, info):
		"""
		获取function信息
		:param info:
		:return:
		"""
		self.line_ = info[1]
		self.module_name = info[2]
		filename = info[0]
		filename = str(filename).split('/')[-1]
		if platform.system().lower() == 'windows'.lower():
			filename = path.split(filename)[1]
		self.filename = filename

	def _create_msg(self, msg, level='DEBUG'):
		"""
		创建信息
		:param msg: 信息
		:param level: 信息级别
		:return:
		"""
		msg1 = ''
		date_ = self.date.split(' ')[0]
		time_ = self.date.split(' ')[1]
		if self.format_date:
			msg1 = date_ + str(msg1)
		if self.format_time:
			msg1 = str(msg1) + ' ' + str(time_)
		if self.format_filename:
			msg1 = msg1 + " " + self.filename
		if self.format_line:
			msg1 = str(msg1) + "  line: " + str(self.line_)
		if self.class_name is not None and self.format_class:
			msg1 = str(msg1) + " - Class: " + str(self.class_name)
		if self.module_name != '<module>' and self.format_fun:
			msg1 = str(msg1) + " Function: " + self.module_name
		if self.format_level:
			msg1 = str(msg1) + " - %s : " % level + msg
		self.msg1 = msg1

	def _wr(self):
		try:
			# 如果开启了文本日志
			if self.txt_mode:
				self.txt_wr.write(self.msg1)
				self.txt_wr.write("\n")
		except Exception as e:
			print(self.Red + str(e) + self.RESET_ALL)

	def _arg(self, arg):
		"""
		解析参数
		:param arg:
		:return:
		"""
		arg_ = ''
		for i in arg:
			arg_ = arg_ + str(i)
		return arg_

	def _get_time(self):
		self.date = str(datetime.now()).split('.')[0]

	def info(self, msg, *arg, **kwarg):
		"""
		打印信息
		:param msg: 打印内容
		:return:
		"""
		fun_info = inspect.getframeinfo(inspect.currentframe().f_back)
		self.fun_info(info=fun_info)
		self._get_time()
		if arg:
			msg = str(msg) + str(self._arg(arg=arg))
		if kwarg:
			msg = str(msg) + str(self._arg(arg=kwarg))
		self._create_msg(msg=msg, level="INFO")
		mess = str(self.Greet + self.msg1 + self.RESET_ALL)
		if self.fileinfo and self.txt_mode:
			mess = str(self.Greet + str(self.file_name) + ' <<-- ' + self.msg1 + self.RESET_ALL)
		if self.level_console <= 1:
			print(mess)
		if self.level_text <= 1:
			self._wr()

	def debug(self, msg, *arg, **kwarg):
		"""
		打印信息
		:param msg: 打印内容
		:return:
		"""
		fun_info = inspect.getframeinfo(inspect.currentframe().f_back)
		self.fun_info(info=fun_info)
		self._get_time()
		if arg:
			msg = str(msg) + str(self._arg(arg=arg))
		if kwarg:
			msg = str(msg) + str(self._arg(arg=kwarg))
		self._create_msg(msg=msg)
		mess = str(self.Blue + self.msg1 + self.RESET_ALL)
		if self.fileinfo and self.txt_mode:
			mess = str(self.Blue + str(self.file_name) + ' <<-- ' + self.msg1 + self.RESET_ALL)
		if self.level_console == 0:
			print(mess)
		if self.level_text <= 0:
			self._wr()

	def warning(self, msg, *arg, **kwarg):
		"""
		打印信息
		:param msg: 打印内容
		:return:
		"""
		fun_info = inspect.getframeinfo(inspect.currentframe().f_back)
		self.fun_info(info=fun_info)
		self._get_time()
		if arg:
			msg = str(msg) + str(self._arg(arg=arg))
		if kwarg:
			msg = str(msg) + str(self._arg(arg=kwarg))
		self._create_msg(msg=msg, level="WARNING")
		mess = str(self.Yellow + self.msg1 + self.RESET_ALL)
		if self.fileinfo and self.txt_mode:
			mess = str(self.Yellow + str(self.file_name) + ' <<-- ' + self.msg1 + self.RESET_ALL)
		if self.level_console <= 2:
			print(mess)
		if self.level_text <= 2:
			self._wr()

	def error(self, msg, *arg, **kwarg):
		"""
		打印信息
		:param msg: 打印内容
		:return:
		"""
		fun_info = inspect.getframeinfo(inspect.currentframe().f_back)
		self.fun_info(info=fun_info)
		self._get_time()
		if arg:
			msg = str(msg) + str(self._arg(arg=arg))
		if kwarg:
			msg = str(msg) + str(self._arg(arg=kwarg))
		self._create_msg(msg=msg, level="ERROR")
		mess = str(self.Red + self.msg1 + self.RESET_ALL)
		if self.fileinfo and self.txt_mode:
			mess = str(self.Red + str(self.file_name) + ' <<-- ' + self.msg1 + self.RESET_ALL)
		if self.level_console <= 3:
			print(mess)
		if self.level_text <= 3:
			self._wr()


class Command:
	def __init__(self, password, cmd=None, terminal=False, logs=False, work=getcwd(), debug=False):
		"""
		执行系统指令
		:param terminal: 是否使用图形终端执行, 全局默认,子功能可自定义
		:param password: 主机密码(string)
		:param cmd: 需要执行的命令(string)
		:param logs: 是否使用日志打印信息
		:param work: 工作目录(string)
		:param debug: 是否打印调试信息
		"""
		self.debug = debug
		self.pwd = getcwd()
		self.logs = logs
		chdir(work)
		# 是否使用终端执行
		self.terminal = terminal
		# 设置主机密码
		self.password = password
		# 传入初始命令
		self.cmd = cmd
		# 生成最终命令
		self.cmd_ = ''
		# 执行结果
		self.ok = False
		# 退出状态码
		self.code = 0

		# 是否有可用终端
		self.use_terminal = False
		# 终端类型
		self.terminal_type = ''
		# 终端参数
		self.terminal_arg = ''
		# home路径
		self.home = getenv('HOME')
		# 临时脚本文件
		self.sh = path.join(self.home, 'run_tmp.sh')
		self.user = getoutput('echo $USER')
		self.uid = getoutput('echo $UID')
		# 系统类型
		self.os_type = platform.system()
		if str(self.os_type).lower() == 'linux'.lower():
			self.os_type = getoutput("""grep ^ID /etc/os-release | sed 's/ID=//' | sed -n 1p | sed 's#\"##g'""")

		# 定义日志文件
		log_file = path.join(self.home, 'cmd_.log')
		# 初始化日志
		self.logger = ColorLogger(file=log_file, txt=logs, class_name=self.__class__.__name__)
		if self.terminal:
			self._get_terminal()

	def create(self):
		"""
		创建命令脚本
		:return:
		"""
		try:
			with open(file=self.sh, mode='w+', encoding='utf-8') as w:
				w.write('#!/bin/bash\n')
				w.write(self.cmd_)
				w.close()
		except Exception as e:
			print(e)
		system("chmod +x %s" % self.sh)
		system("cat %s" % self.sh)

	def _get_terminal(self):
		"""
		获取终端类型
		:return:
		"""
		t_ = {'mate-terminal': '-e', 'gnome-terminal': '-e', 'deepin-terminal': '-C'}
		for i in t_:
			cmd = "which %s" % str(i)
			if int(getstatusoutput(cmd)[0]) == 0:
				self.terminal_type = str(i)
				self.terminal_arg = t_[i]
				self.use_terminal = True
				# print('存在终端程序: %s' % str(self.terminal_type))
				return True
			# print('找不到终端程序: %s' % str(self.terminal_type))
			self.terminal = False
		return False

	def terminal_fun(self):
		"""
		使用终端执行命令
		:return:
		"""
		if self._get_terminal():
			cmd = """%s %s '%s;read -p "请按回车关闭此界面"'""" % (self.terminal_type, self.terminal_arg, self.cmd_)
			if self.logs and self.debug:
				self.logger.debug(cmd)
			getoutput(cmd)
		else:
			print('找不到终端类型,当前系统类型: %s' % self.os_type)

	def shell(self, cmd=None, terminal=None):
		"""
		执行普通Shell命令
		:param terminal: 是否使用终端
		:param cmd: 需要执行的命令，默认使用实例初始命令
		:return: 当使用父进程的时候，返回执行结果(bool)
		"""
		if cmd is None:
			cmd = self.cmd
		if terminal is None:
			terminal = self.terminal
		self.cmd_ = cmd
		if self.logs and self.debug:
			self.logger.debug(self.cmd_)
		else:
			if self.debug:
				print(self.cmd_)
		if terminal and self.use_terminal:
			self.terminal_fun()
		else:
			try:
				self.code = system(self.cmd_)
			except Exception as e:
				self.logger.error(str(e))
			if int(self.code) == 0:
				return True
			return False

	def sudo(self, cmd=None, terminal=None, name=None):
		"""
		执行sudo命令
		:param cmd: 需要执行的命令，默认使用实例初始命令
		:param terminal: 是否使用终端执行(创建新进程,无法获取执行结果),默认使用实例值
		:param name: 任务名称
		:return: 当 terminal 等于 True则直接返回True, 否则返回命令结果(bool),同时将退出代码赋予self.code
		"""
		if cmd is None:
			cmd = self.cmd
		if terminal is None:
			terminal = self.terminal
		self.cmd_ = str("""echo %s | sudo -S %s""" % (self.password, cmd))
		if str(self.user).lower() == 'root' or str(self.uid) == '0':
			self.cmd_ = cmd
		if terminal and self.use_terminal:
			self.terminal_fun()
			if name:
				print("[ %s ] 已通过终端执行" % str(name))
			return True
		else:
			mess = '执行成功'
			ok = True
			try:
				self.code = system(self.cmd_)
			except Exception as e:
				self.logger.error(str(e))
			if int(self.code) != 0:
				mess = '执行失败'
				ok = False
			if name is None:
				name = self.cmd_
			print("[ %s ] %s" % (str(name), str(mess)))
			self.ok = ok
			return self.ok

	def getout(self, cmd=None):
		"""
		获取命令输出
		:param cmd: 需要执行的命令，默认使用实例初始命令
		:return: 返回执行过程数据(str),执行结果通过self.code获取
		"""
		if cmd is None:
			cmd = self.cmd
		if self.logs and self.debug:
			self.logger.debug(cmd)
		i = getstatusoutput(cmd)
		self.code = i[0]
		return i[1]

	def getout_sudo(self, cmd=None, name=None, debug=None, mess_ok=None, mess_failed=None):
		"""
		获取sudo权限命令输出
		:param mess_failed: 执行失败的信息,默认: 执行失败
		:param mess_ok: 执行成功的信息,默认: 执行成功
		:param debug: debug
		:param name: 任务名称,默认: 完整命令
		:param cmd: 需要执行的命令，默认使用实例初始命令
		:return: 返回执行过程数据(str),执行结果通过self.code获取
		"""
		if mess_ok is None:
			mess_ok = '执行成功'
		if mess_failed is None:
			mess_failed = '执行失败'
		if cmd is None:
			cmd = self.cmd
		cmd = str("echo %s | sudo -S %s" % (self.password, cmd))
		if name is None:
			name = cmd
		if debug:
			self.logger.debug(cmd)
		if debug is None:
			debug = self.logs
		i = getstatusoutput(cmd)
		self.code = i[0]
		if int(self.code) == 0:
			if debug:
				print("[ %s ] %s" % (str(name), str(mess_ok)))
		else:
			if debug:
				print("[ %s ] %s" % (str(name), str(mess_failed)))
		return i[1]

	def echo_to_file(self, file, cmd):
		"""
		追加echo结果到文件
		:param file: 例如: /etc/sysctl.conf
		:param cmd: 例如: echo 123
		:return:
		"""
		system("echo {0} | sudo -S pwd".format(self.password))
		cmd = str("{0} | sudo tee -a {1}".format(cmd, file))
		self.shell(cmd=cmd, terminal=False)

	def add_path(self, paths):
		"""
		追加path路径到用户变量
		:param paths: 需要追加的path
		:return:
		"""
		path_list = getenv("PATH").lower().split(":")
		if str(paths).lower() in path_list:
			if self.debug:
				self.logger.debug("Path env is Exists")
			return False
		else:
			print("Add [ %s ] To PATH" % paths)
			file = path.join(self.home, '.bashrc')
			try:
				w = open(file=file, mode='w+', encoding='utf8')
				txt_path = str("\nexport PATH=${PATH}:%s" % paths)
				w.write(txt_path)
				w.close()
				return True
			except Exception as e:
				self.logger.error("add_path: %s " % str(e))
				return False


class NetManagement(object):
	def __init__(self, password=None, ip=None, gw=None, mask=24, dns1=None, dns2=None, net=None, dev=None, debug=True):
		"""
		网络管理模块,参数均为可选传参，请根据实际需求传入
		:param password: (str, optional): 设置主机密码. Defaults to None.
		:param ip: (str, optional): 设置IP地址. Defaults to "192.168.1.138".
		:param gw: (str, optional): 设置网关. Defaults to "192.168.1.1".
		:param mask: (int, optional): 设置子网掩码. Defaults to 24.
		:param dns1: (str, optional): 设置DNS1. Defaults to "114.114.114.114".
		:param dns2: (str, optional): 设置DNS2. Defaults to "119.29.29.29".
		:param net: (str, optional): 设置网段,一般是自动尝试配置IP需要. Defaults to '192.168.1.'.
		:param dev: (str, optional): 设置网卡名称. Defaults to 'eth0'.
		:param debug: (bool, optional): 是否启用日志功能
		"""
		self.DefaultDnsList = None  # 默认网卡DNS地址列表
		self.DefaultMask = None  # 默认掩码
		self.DefaultIp4 = None  # 默认IP地址
		self.DefaultGw = None  # 默认网关地址
		if ip is None:
			ip = "192.168.1.138"
		if gw is None:
			gw = "192.168.1.1"
		if dns1 is None:
			dns1 = "114.114.114.114"
		if dns2 is None:
			dns2 = "119.29.29.29"
		if net is None:
			net = "192.168.1."
		if dev is None:
			dev = 'eth0'
		self.debug_ = debug
		# 主机密码
		self.password = password
		# 网段
		self.subnet = net
		# IP地址
		self.ipv4 = ip
		# 网关
		self.gateway = gw
		# DNS1
		self.dns1 = dns1
		# DNS2
		self.dns2 = dns2
		# 子网掩码
		self.netmask = mask
		# 连接名称
		self.connect_name = 'Y'
		# 连接模式
		self.connect_mode = 'auto'
		# DNS列表
		self.dns_list = []
		# 网卡设备
		self.device = dev
		self.DefaultDev = None  # 默认网卡设备名称
		self.deviceList = []  # 网卡列表
		self.logger = ColorLogger()
		# ju = Jurisdiction(passwd=password)
		# if not ju.verification(name='NetManagement'):
		# 	self.logger.error('密码错误/用户无权限')
		# 	exit(1)
		self.cmd = Command(password=password, debug=self.debug_)

	def connect_create(self, name="Y", mode='auto'):
		"""
		创建连接
		:param name: (str, optional): 连接名称. Defaults to "Y".
		:param mode: (str, optional): 连接模式. Defaults to "auto".
		:return:
		"""
		c = str("""nmcli connection add type ethernet  con-name {0} ifname {1}""".format(name, self.device))
		get = os.system(c)
		if get == 0:
			self.logger.info("连接创建成功")
		else:
			print(c)
			self.logger.error("连接创建失败")
			return False
		if mode != "auto":
			if self.debug_:
				self.logger.debug("使用静态IP配置")
			c = str("""nmcli connection modify {0} ipv4.method manual ipv4.addresses {1}""".format(name, self.ipv4))

		else:
			if self.debug_:
				self.logger.debug("使用自动获取IP")
			c = str("""nmcli connection modify {0} ipv4.method auto""".format(name))
		self.run(c=c, n_="IP获取模式配置")

	def run(self, c, n_):
		"""
		运行命令
		:param c: 需要执行的命令
		:param n_: 命令名称
		:return: bool
		"""
		get = self.cmd.shell(cmd=c)
		if get:
			self.logger.info("[ %s ]成功" % n_)
			return True
		else:
			print(c)
			self.logger.warning("[ %s ]失败" % n_)
		return False

	def GetConUuid(self, name):
		"""
		通过名称或者关键词获取一个连接的UUID
		:param name:
		:return:
		"""
		c = str("""nmcli connection  | grep "%s" """ % name)
		get = self.cmd.getout(c)
		if self.cmd.code == 0:
			sp = str(get).split(" ")
			sp = ListGetLen(sp, 10)  # 获取元素长度大于10的元素
			if len(sp) == 1:
				return sp[0]
			if len(sp) > 1:
				for i in sp:
					ts = str(i).split("-")
					if len(ts) == 5:
						return i
		return False

	def ConnectDelete(self, con):
		"""
		删除连接
		:param con: 需要删除的连接名称/名称关键词或者UUID
		:return: bool
		"""
		if con is None:
			con = self.connect_name
		cl = self.GetConUuid(name=con)
		if cl:
			c = str("""nmcli connection  delete %s""" % cl)
			self.cmd.shell(c)
			if self.cmd.code != 0:
				self.logger.error("删除失败")
				return False
		else:
			self.logger.warning("无法获取连接")
		return True

	def GetDevList(self):
		"""
		获取设备列表
		:return:bool(数据请通过 self.deviceList 获取)
		"""
		c = str("""ip link show up | grep '<' | sed 's/://g' | awk '{print $2}' | grep -v ^docker | grep -v ^lo""")
		get = self.cmd.getout(c)
		if self.cmd.code == 0:
			self.deviceList = str(get).split("\n")
			if self.debug_:
				self.logger.debug(str(self.deviceList))
			if len(self.deviceList) >= 1:
				self.logger.info("网卡信息获取成功")
				return True
		self.logger.error("无法获取网卡设备信息")
		return False

	def _getIp(self, lr: list):
		"""
		从列表数据中获取IPV4地址
		:param lr: 需要获取的字符串
		:return: 返回IP地址或者False
		"""
		for i in lr:
			try:
				ip = ipaddress.IPv4Address(i)
				return ip
			except:
				continue
		self.logger.warning("无法获取到IP地址")
		return False

	def GetEthDns(self, eth):
		"""
		获取当前系统默认DNS信息
		:param eth: 获取指定网卡设备的DNS地址
		:return: list
		"""
		# d = []
		c = str("""nmcli device show %s  | grep DNS | awk '{print $2}'""" % eth)
		get = self.cmd.getout(cmd=c)
		if self.cmd.code == 0:
			# print(get)
			sp = str(get).split("\n")
			if len(sp) >= 1:
				return sp
		print(c)
		self.logger.warning("无法获取网卡的DNS信息: %s" % eth)
		return False

	def GetDefaultDev(self):
		"""
		获取默认网卡设备信息，获取到的信息请通过实例变量(Default开头)获取(网卡名称、网关、子网掩码、IP地址)
		:return: bool
		"""
		c = str("ip r | grep default |sed -n 1p")
		get = self.cmd.getout(cmd=c)
		if self.cmd.code == 0:
			sp = str(get).split(" ")
			if self.debug_:
				self.logger.debug(str(sp))
			if len(sp) >= 3:
				if self.debug_:
					self.logger.debug(str(sp))
				self.DefaultDev = sp[4]
				self.DefaultGw = sp[2]
				self.DefaultIp4 = sp[8]
				# 获取DNS信息
				self.DefaultDnsList = self.GetEthDns(self.DefaultDev)
				# 获取子网掩码
				c = str("""ip addr show %s  | grep inet | sed -n 1p | awk '{print $2}'""" % self.DefaultDev)
				get = self.cmd.getout(c)
				if self.cmd.code == 0:
					self.DefaultMask = str(get).split("/")[-1]
				else:
					self.logger.error(get)
				if self.debug_:
					self.logger.debug(c)
				return True
			else:
				print(sp)
				print(c)
				self.logger.warning("无法获取默认设备信息")
		return False

	def GetConListAllUuid(self):
		"""
		获取所有可用的连接UUID列表,返回数据格式: ['7dc597e8-23ad-4360-8dc3-87058a2d08aa', 'b3a484ff-73a6-4e0e-8c1a-0e829b36a848']
		:return: bool/list
		"""
		c = """nmcli con  show --active | grep -v loopback| grep -v ^NAME | grep -v ^docker0  | awk '{print $2}'"""
		get = self.cmd.getout(c)
		if self.cmd.code == 0:
			sp = str(get).split("\n")
			if len(sp) >= 1:
				return sp
		return False

	def GetConListEthUuid(self, eth):
		"""
		获取指定网卡的连接配置的UUID列表,返回数据格式: ['7dc597e8-23ad-4360-8dc3-87058a2d08aa', 'b3a484ff-73a6-4e0e-8c1a-0e829b36a848']
		:return:bool/list
		"""
		c = str("""nmcli connection show | grep %s | awk '{print $2}'""" % eth)
		get = self.cmd.getout(c)
		if self.cmd.code == 0:
			sp = str(get).split("\n")
			if len(sp) >= 1:
				return sp
		return False

	def GetEthInfo(self, eth):
		"""
		获取指定网卡的网络配置信息,包含: IP4、网关、掩码、DNS
		:param eth: 需要获取的网卡名称
		:return:
		"""
		c = str("""ip address show %s  | grep inet | sed -n 1p  | awk '{print $2}'""" % eth)
		info = {}
		get = self.cmd.getout(c)
		if self.cmd.code == 0:
			i = str(get).split("/")
			if len(i) == 2:
				info["mask"] = i[1]
				info["ip"] = i[0]
			else:
				self.logger.warning("IP和掩码获取失败")
		c = str("""nmcli device show %s  | grep IP4.GATE | awk '{print $2}'""" % eth)
		get = self.cmd.getout(c)
		if self.cmd.code == 0:
			info["gw"] = get
		else:
			self.logger.warning("网关获取失败")
			info["gw"] = None
		dns = self.GetEthDns(eth=eth)
		if dns:
			info["dns"] = dns
		else:
			info["dns"] = []
			self.logger.warning("DNS获取失败")
		return info

	def GetConDns(self, con):
		"""
		获取指定连接的DNS信息
		:param con: 需要获取的连接名称或者uuid
		:return:list
		"""
		c = str("""nmcli con show %s  | grep IP4.DNS | awk '{print $2}'""" % con)
		if self.debug_:
			self.logger.debug(c)
		get = self.cmd.getout(c)
		if self.cmd.code == 0:
			ds = str(get).split("\n")
			if self.debug_:
				self.logger.debug("ds: ", str(ds))
			if len(ds) >= 1:
				return ds
		self.logger.warning("无法获取连接的DNS信息")
		return [None]

	def GetConGw(self, con):
		"""
		获取连接的网关信息
		:param con: 需要获取的连接名称或者uuid
		:return:
		"""
		c = str("""nmcli con show %s  | grep IP4.GATE | awk '{print $2}'""" % con)
		if self.debug_:
			self.logger.debug(c)
		get = self.cmd.getout(c)
		if self.cmd.code == 0:
			return get
		else:
			self.logger.warning("网关获取失败")
		return None

	def GetConIp(self, con):
		"""
		获取连接的IP地址
		:param con: 需要获取的连接名称或者uuid
		:return:
		"""
		info = {}
		c = str("""nmcli con show %s  | grep IP4.ADD | awk '{print $2}'""" % con)
		if self.debug_:
			self.logger.debug(c)
		get = self.cmd.getout(c)
		if self.cmd.code == 0:
			sp = str(get).split("/")
			if self.debug_:
				self.logger.debug(get)
				self.logger.debug(str(sp))
			if len(sp) == 2:
				info["ip"] = str(sp[0])
				info["mask"] = int(sp[1])
				return info

		else:
			self.logger.warning("网关获取失败")
		info["ip"] = None
		info["mask"] = 0
		return info

	def GetConInfo(self, con):
		"""
		获取指定连接的网络配置信息,通过status判断获取结构, 格式: {'ip': '10.1.1.18', 'mask': 24, 'gw': '10.1.1.1', 'dns': ['10.1.1.1'], 'status': True}
		:param con: 连接名称或UUID
		:return: dict
		"""
		info = {}
		# 首先获取UUID
		c = self.GetConUuid(name=con)
		if c:
			ip = self.GetConIp(con=con)
			info = ip
			gw = self.GetConGw(con=con)
			info["gw"] = gw
			dns = self.GetConDns(con=con)
			if self.debug_:
				self.logger.debug(str(dns))
			info["dns"] = dns
			info["status"] = True
			return info
		else:
			self.logger.error("无法获取连接信息,请检查配置参数")
			info["status"] = False
		return info

	def SetConGw(self, con):
		"""
		设置连接的网关地址
		:param con: 连接名称/UUID
		:return:
		"""
		c = str("""nmcli connection modify %s ipv4.gateway %s""" % (con, self.gateway))
		get = self.cmd.shell(cmd=c)
		if not get:
			self.logger.debug(c)
			self.logger.error("网关配置失败")
			return False
		return True

	def SetConIp(self, con):
		"""
		设置连接的IP地址
		:param con: 连接名称/UUID
		:return: bool
		"""
		c = str(
			"""nmcli connection modify %s ipv4.method manual ipv4.addresses %s/%d""" % (con, self.ipv4, self.netmask))
		get = self.cmd.shell(cmd=c)
		if not get:
			self.logger.debug(c)
			self.logger.error("IP设置失败")
			return False
		return True

	def SetConDns(self, con):
		"""
		设置连接的DNS地址
		:param con: 连接名称/UUID
		:return: bool
		"""
		s = 0
		for i in [self.dns1, self.dns2]:
			if int(s) == 0:
				cmd = str("""nmcli connection modify %s ipv4.dns %s""" % (con, i))
			else:
				cmd = str("""nmcli connection modify %s +ipv4.dns %s""" % (con, i))
			self.cmd.shell(cmd=cmd)
			if self.cmd.code != 0:
				self.logger.debug(cmd)
				self.logger.error("DNS设置失败")
				return False
			s += 1
		return True

	def ConnectAddStatic(self, con, dev=None):
		"""
		添加新的静态连接
		:param dev: 需要指定的设备
		:param con: 连接名称
		:return: bool
		"""
		if dev is None:
			dev = self.device
		self.ConnectDelete(con=con)
		c = str("""nmcli connection add type ethernet  con-name %s ifname %s""" % (con, dev))
		get = self.cmd.shell(cmd=c)
		if not get:
			self.logger.debug(c)
			self.logger.error("连接创建失败")
			sys.exit(4)
		# 添加IP
		if self.SetConIp(con=con):
			# 添加网关
			if self.SetConGw(con=con):
				if self.SetConDns(con=con):
					return self.ConUp(con=con)
		return False

	def DevReApply(self):
		"""
		重新应用设备
		:return:
		"""
		c = str("""nmcli device reapply %s""" % self.device)
		get = self.cmd.shell(cmd=c)
		if not get:
			self.logger.debug(c)
			self.logger.error("连接创建失败")
			return False
		return True

	def ConUp(self, con):
		"""
		启用连接
		:param con: 连接名称/UUID
		:return:
		"""
		u = self.GetConUuid(name=con)
		if u:
			c = str("""nmcli connection up uuid %s""" % u)
			get = self.cmd.shell(cmd=c)
			if not get:
				self.logger.debug(c)
				self.logger.error("连接启用失败")
				return False
			return self.DevReApply()
		else:
			self.logger.warning("无法获取连接UUID信息")
			return False


class ReIp:
	def __init__(self, a, b, sub, gw, mask, wlan, ds: list, passwd=None):
		"""
		:param a: 需要遍历的IP起始地址
		:param b: 需要遍历的IP结束地址
		:param sub: 设置子网(10.1.1)
		:param gw: 设置网关(10.1.1.1)
		:param mask: 设置掩码(255.255.255.0)
		# :param eth: 设置网卡名称
		:param wlan: 是否设置无线网卡
		:param ds: 设置DNS列表
		:param passwd: 设置系统密码(部分Linux系统需要sudo才能操作nmcli)
		"""
		self.eth = None
		self.ds = ds
		self.wlan = wlan
		# self.eth = eth
		self.mask = mask
		self.gw = gw
		self.sub = sub
		self.b = b
		self.a = a
		self.wireless_adapter = None
		self.ip = None
		self.passwd = passwd
		self.nmcli = NetManagement(password=self.passwd, debug=argDebug)
		self.logger = ColorLogger(class_name=self.__class__.__name__)

	def selectEth(self):
		"""
		选择网卡设备
		:return:
		"""
		eth = self.nmcli.DefaultDev
		# print(eth)
		while True:
			s = 1
			info = {}
			for i in self.nmcli.deviceList:
				print(s, ": ", i)
				info[s] = i
				s += 1
			if eth is None:
				us = input("请输入需要配置的网卡设备编号:\n")
			else:
				us = input("请输入需要配置的网卡设备编号(默认名称: %s)\n" % eth)
			try:
				us = int(us)
			except Exception as e:
				self.logger.warning("请输入编号(整数): ", str(e))
				continue
			if int(us) > s:
				self.logger.warning("输入大于实际,请重新输入")
				continue
			if int(us) < 1:
				self.logger.warning("最小值是1,请重新输入")
				continue
			self.eth = info[us]
			self.logger.info("选择了: ", self.eth)
			break

	def GetEth(self):
		"""
		获取默认网卡设备
		:return:
		"""
		eth = None
		self.nmcli.debug = True
		getList = self.nmcli.GetDevList()
		if not getList:
			self.logger.error("无法获取网卡信息")
			sys.exit(1)
		# 如果没有设置网卡，则使用默认网卡
		self.nmcli.GetDefaultDev()
		if getList:
			self.logger.info("正在进入网卡选择")
			self.selectEth()

	# if self.eth is None:
	# 	self.logger.info("未设置网卡")
	# 	# 如果没有设置网卡，则使用默认网卡
	# 	self.nmcli.GetDefaultDev()
	# 	if getList:
	# 		self.logger.info("正在进入网卡选择")
	# 		self.selectEth()
	# else:
	# 	self.logger.info("正在检测指定的网卡是否存在")
	# 	print(self.eth)
	# 	if self.eth not in self.nmcli.deviceList:
	# 		self.nmcli.GetDefaultDev()
	# 		self.selectEth()
	def ping(self):
		"""
		开始进行Ping检测
		:return:
		"""
		global argPack
		c = "ping -c %d %s" % (argPack, self.gw)
		self.logger.info("正在进行Ping检测...")
		if argDebug:
			print(c)
		run = getstatusoutput(c)
		if run[0] == 0:
			pack = GetPingPacket(run[1])
			self.logger.debug(str(pack))
			if pack[0]:
				if int(pack[1]) == 0:
					self.logger.info("当前IP丢包率: %s" % pack[1])
					return True
				self.logger.warning("当前丢包率: %s" % pack[1])

			else:
				self.logger.warning("无法匹配丢包率")
		else:
			self.logger.error("Ping检测失败")
		return False

	def start(self):
		self.GetEth()
		for i in range(self.a, self.b):
			self.ip = self.sub + "." + str(i)
			self.nmcli.ipv4 = self.ip
			self.nmcli.gateway = self.gw
			self.nmcli.netmask = NetMaskGenCode(self.mask)
			self.nmcli.dns1 = self.ds[0]
			self.nmcli.dns2 = self.ds[1]
			self.nmcli.device = self.eth
			logger.info("当前生成IP: %s" % self.ip)
			if self.nmcli.ConnectAddStatic(con="static"):
				if self.ping():
					WriteConfig(file=cf)
					break
			if int(i) < int(self.b):
				self.logger.debug("正在切换IP进行测试")


def GetPingPacket(txt: str):
	"""
	从ping结果中匹配丢包率
	:param txt:
	:return: (bool/int)
	"""
	print(txt)
	p = r"(\d+)(% )"
	m = re.findall(p, txt)
	if len(m) == 1:
		if len(m[0]) == 2:
			return True, m[0][0]
	return False, -1


def _dic():
	info = {16: "255.255.0.0",
	        17: "255.255.128.0",
	        18: "255.255.192.0",
	        19: "255.255.224.0",
	        20: "255.255.240.0",
	        21: "255.255.248.0",
	        22: "255.255.252.0",
	        23: "255.255.254.0",
	        24: "255.255.255.0",
	        25: "255.255.255.128",
	        26: "255.255.255.192",
	        27: "255.255.255.224",
	        28: "255.255.255.240",
	        29: "255.255.255.248",
	        30: "255.255.255.252",
	        31: "255.255.255.254",
	        32: "255.255.255.255"}
	return info


def NetMaskGenCode(mask: str):
	"""
	通过IP掩码地址获取掩码位数,例如: 255.255.255.0返回24
	:param mask:
	:return: int
	"""
	info = _dic()
	for i in info:
		if mask == info[i]:
			return int(i)
	return False


def ReadConfig(file, info_: dict):
	"""
	读取配置文件
	:param file: 需要读取的文件
	:param info_: 默认信息
	"""
	global DefaultSub, DefaultStop, DefaultEth, DefaultDns1, DefaultDns2, DefaultGw, DefaultMask, DefaultStart
	if path.isfile(file):
		try:
			with open(file=cf, mode="r", encoding="utf8") as r:
				txt = r.read()
				r.close()
			sp = str(txt).split("\n")
			if len(sp) == 7:
				for i in sp:
					i = str(i).replace(" ", "")
					if len(i) >= 2:
						kv = str(i).split("=")
						if len(kv) == 2:
							info[kv[0]] = kv[1]
				return True, info
			else:
				logger.warning("配置文件参数数量不符合标准")
		except Exception as e:
			logger.error(str(e))
	else:
		logger.warning("文件不存在: ", file)
	return False, info_


def IsIpv4(ip: str):
	"""
	检查是否属于标准的IPV4地址,返回布尔值和最终IP地址
	:param ip: 需要判断的IP地址
	:return: (bool, ip)
	"""
	tmp = ""
	s = 1
	sp = str(ip).split(".")
	if len(sp) != 4:
		print("IP格式错误")
		return False, "IP格式错误"
	for i in sp:
		try:
			i = int(i)
		except Exception as e:
			print(e)
			print("IP格式错误,请传入由整数和.组成的IPV4地址")
			return False, "IP格式错误,请传入由整数和.组成的IPV4地址"
		if int(i) > 255:
			print("存在值大于255的IP元素")
			return False, "存在值大于255的IP元素"
		if int(i) < 0:
			print("存在负整数")
			return False, "存在负整数"
		if len(str(i)) == 2:
			if str(i)[0] == 0:
				print("存在0开头的元素")
				return False, "存在0开头的元素"
		if s == 4:
			tmp = tmp + str(i)
		else:
			tmp = tmp + str(i) + "."
		s += 1
	return True, tmp


def IpCutSub(ip: str):
	"""
	:param ip:
	:return: str/bool
	"""
	if IsIpv4(ip=ip)[0]:
		sp = str(ip).split(".")
		sub = str(sp[0]) + "." + str(sp[1]) + "." + str(sp[2])
		return sub
	return False


def SetDefaultNet():
	"""
	设置默认网络信息
	:return:
	"""
	global DefaultSub, DefaultStop, DefaultEth, DefaultDns1, DefaultDns2, DefaultGw, DefaultMask, DefaultStart
	nmcli = NetManagement(debug=False)
	if nmcli.GetDefaultDev():
		logger.debug("正在自动获取IP信息")
		DefaultDns1 = nmcli.dns1
		DefaultDns2 = nmcli.dns2
		DefaultGw = nmcli.DefaultGw
		DefaultSub = IpCutSub(DefaultGw)
		return True
	return False


def handelConfig(info_: dict):
	"""
	处理数据
	:param info_:
	:return:
	"""
	global DefaultSub, DefaultStop, DefaultEth, DefaultDns1, DefaultDns2, DefaultGw, DefaultMask, DefaultStart
	if not SetDefaultNet():
		if "sub" in info_:
			DefaultSub = info_["sub"]
		if "gw" in info_:
			DefaultGw = info_["gw"]
		if "dns1" in info_:
			DefaultDns1 = info_["dns1"]
		if "dns2" in info_:
			DefaultDns2 = info_["dns2"]
	# print(info_)
	if "start" in info_:
		DefaultStart = int(info_["start"])
	if "stop" in GetConf:
		DefaultStop = int(info_["stop"])
	if "mask" in info_:
		DefaultMask = info_["mask"]
	if "eth" in info_:
		DefaultEth = info_["eth"]


def WriteConfig(file: str):
	"""
	写入配置
	:param file: 需要写入的文件路径
	:return: bool
	"""
	infoDic = {"start": argA, "stop": argB, "sub": argSub, "mask": argMask, "gw": argGw,
	           "dns1": argD1, "dns2": argD2}
	txt = ""
	try:
		with open(file=file, mode="w", encoding="utf8") as w:
			line = 1
			for i in infoDic:
				if line == 1:
					txt = str(i) + "=" + str(infoDic[i])
				else:
					txt = txt + "\n" + str(i) + "=" + str(infoDic[i])
				line += 1
			w.write(txt)
			w.close()

	except Exception as e:
		logger.warning(str(e))
	return False


if __name__ == "__main__":
	UseFileConfig = False
	logger = ColorLogger()
	userHome = environ["HOME"]
	cf = path.join(userHome, "ReIp.conf")
	DefaultStart = 2
	DefaultStop = 254
	DefaultSub = "10.1.17"
	DefaultMask = "255.255.255.0"
	DefaultGw = "10.1.17"
	DefaultDns1 = "119.29.29.29"
	DefaultDns2 = "180.76.76.76"
	DefaultEth = "eth0"
	info = {"start": DefaultStart, "stop": DefaultStop, "sub": DefaultSub, "mask": DefaultMask, "gw": DefaultGw,
	        "d1": DefaultDns1, "d2": DefaultDns2, "eth": DefaultEth}
	GetConf = ReadConfig(cf, info_=info)
	if GetConf[0]:
		logger.debug("配置读取成功")
		handelConfig(GetConf[1])
	else:
		SetDefaultNet()
	arg = ArgumentParser(description='reip: 适用于局域网内自动化切换静态IP地址', prog="reip")
	arg.add_argument('-a', type=int, default=DefaultStart, help='设置起始IP值, 默认: %d' % DefaultStart, required=False)
	arg.add_argument('-b', type=int, default=DefaultStop, help='设置结束IP值，默认：%d' % DefaultStop, required=False)
	helpS = '设置IP段，默认: ' + str(DefaultSub)
	arg.add_argument('-sub', type=str, default=DefaultSub, help=helpS, required=False)
	helpS = '设置网关，默认: ' + str(DefaultGw)
	arg.add_argument('-gw', type=str, default=DefaultGw, help=helpS, required=False)
	helpS = '设置子网掩码，默认: ' + str(DefaultMask)
	arg.add_argument('-m', type=str, default=DefaultMask, help=helpS, required=False)
	helpS = '设置首选DNS，默认: ' + str(DefaultDns1)
	arg.add_argument('-d1', type=str, default=DefaultDns1, help=helpS, required=False)
	helpS = '设置备用DNS，默认: ' + str(DefaultDns2)
	arg.add_argument('-d2', type=str, default=DefaultDns2, help=helpS, required=False)
	helpStr = '设置网卡名称,有线默认: %s，无线默认: WLAN' % DefaultEth
	arg.add_argument('-p', type=int, default=5, help='设置网络发包数量，默认：5', required=False)
	arg.add_argument('-w', action="store_true", help='启用无线网卡模式,默认使用有线网卡')
	arg.add_argument('-d', action="store_true", help='启用调试模式')
	arg.add_argument('-c', type=str, default=cf, help="设置配置文件,默认: %s" % cf, required=False)
	args = arg.parse_args()
	argA = args.a
	argB = args.b
	argDebug = args.d
	argPack = args.p
	argSub = args.sub
	argGw = args.gw
	argMask = args.m
	argWlan = args.w
	argD1 = args.d1
	argD2 = args.d2
	ps = str(argSub).split(".")
	if len(ps) != 3:
		logger.error("子网格式设置错误,标准格式: 10.1.1")
		sys.exit(3)
	dns = [argD1, argD2]
	if argWlan:
		argEth = "WLAN"
	r = ReIp(a=argA, b=argB, sub=argSub, gw=argGw, mask=argMask, wlan=argWlan, ds=dns)
	r.start()
